using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using Newtonsoft.Json.Linq;

namespace PlurkApi
{
    /// <summary>
    /// 
    /// </summary>
    public class PlurkApi
    {
        private readonly WebUtil web;
        private Cookie cookie;

        /// <summary>
        /// your plurk nickname
        /// </summary>
        public string username { get; set; }

        /// <summary>
        /// your plurk login password
        /// </summary>
        public string password { get; set; }

        /// <summary>
        /// your plurk user id
        /// </summary>
        public int uid { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is logged.
        /// </summary>
        public bool isLogged { get; set; }

        /// <summary>
        /// Gets or sets the API key.
        /// </summary>
        public string apiKey { get; set; }

        private string apiUrl
        {
            get { return "http://www.plurk.com/API"; }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="PlurkApi"/> class.
        /// </summary>
        /// <param name="apiKey">The API key.</param>
        public PlurkApi(string apiKey)
        {
            this.apiKey = apiKey;
            web = new WebUtil();
        }

        /// <summary>
        /// personal inforamtion
        /// </summary>
        public user_info userInfo { get; set; }

        #region Users

        /// <summary>
        /// Register a new Plurk account. Should be HTTPS.
        /// </summary>
        /// <param name="nick_name">The user's nick name. Should be longer than 3 characters. Should be ASCII. Nick name can only contain letters, numbers and _.</param>
        /// <param name="full_name">Can't be empty.</param>
        /// <param name="Password">Should be longer than 3 characters.</param>
        /// <param name="gender">Should be male or female.</param>
        /// <param name="date_of_birth">Should be YYYY-MM-DD, example 1985-05-13.</param>
        /// <param name="email">Must be a valid email.</param>
        /// <returns></returns>
        public user_info register(string nick_name, string full_name, string Password, string gender, DateTime date_of_birth, string email)
        {
            if (string.IsNullOrEmpty(nick_name) || nick_name.Length < 3 || Regex.IsMatch(nick_name, "([a-zA-Z0-9_]+)") == false) { return null; }

            if (string.IsNullOrEmpty(full_name)) { return null; }

            if (string.IsNullOrEmpty(Password) || Password.Length < 3) { return null; }

            if (string.IsNullOrEmpty(gender) || (string.Compare(gender, "male", true) == 0 || string.Compare(gender, "female", true) == 0) == false) { return null; }

            const string apiAction = "Users/register";
            var url = string.Format("{0}/{1}?api_key={2}&nick_name={3}&full_name={4}&password={5}&gender={6}&date_of_birth={7}&email={8}", "https://www.plurk.com/API", apiAction, apiKey, nick_name, full_name, Password, gender, date_of_birth.ToString("yyyy-MM-dd"), email);

            string data;
            try { data = web.GetPage(url, "post", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            return (data.Contains("error_text")) ? null : new user_info(data);
        }

        /// <summary>
        /// Login an already created user. Login creates a session cookie, which can be used to access the other methods. On success it returns the data returned by /API/Profile/getOwnProfile. Should be HTTPS.
        /// </summary>
        /// <param name="Username">The user's nick name or email.</param>
        /// <param name="Password">The user's password.</param>
        /// <returns></returns>
        public bool login(string Username, string Password)
        {
            username = Username;
            password = Password;
            const string apiAction = "Users/login";
            var url = string.Format("{0}/{1}?api_key={2}&username={3}&password={4}", "https://www.plurk.com/API", apiAction, apiKey, username, password);

            string data;
            try { data = web.GetPage(url, "post", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            JObject obj;
            try { obj = JObject.Parse(data); }
            catch (Exception) { return false; }

            if (obj["user_info"]["uid"] != null)
            {
                userInfo = new user_info(obj["user_info"].ToString());
                uid = int.Parse(userInfo.ToString()); 
                isLogged = (uid != -1) ? true : false;
                return isLogged;
            }

            return false;
        }

        /// <summary>
        /// Logouts this instance.
        /// </summary>
        /// <returns></returns>
        public bool logout()
        {
            const string apiAction = "Users/logout";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            isLogged = (data.Contains("success_text")) ? false : true;

            return (isLogged == false);
        }

        /// <summary>
        /// Update a user's information (such as email, password or privacy). Should be HTTPS.
        /// </summary>
        /// <param name="current_password">User's current password.</param>
        /// <param name="values">
        ///   Key    :    Value
        /// full_name: Change full name.
        /// new_password: Change password.
        /// email: Change email.
        /// display_name: User's display name, can be empty and full unicode. Must be shorter than 15 characters.
        /// privacy: User's privacy settings. The option can be world (whole world can view the profile), only_friends (only friends can view the profile) or only_me (only the user can view own plurks).
        /// date_of_birth: Should be YYYY-MM-DD, example 1985-05-13.
        /// </param>
        /// <returns></returns>
        public bool update(string current_password, Dictionary<string, string> values)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Users/update";
            var url = new StringBuilder(string.Format("{0}/{1}?api_key={2}&current_password={3}", "https://www.plurk.com/API", apiAction, apiKey, current_password));

            foreach (var value in values)
            {
                switch (value.Key)
                {
                    case "full_name":
                        url.AppendFormat("&full_name={0}", value.Value);
                        break;

                    case "new_password":
                        url.AppendFormat("&new_password={0}", value.Value);
                        break;

                    case "email":
                        url.AppendFormat("&email={0}", value.Value);
                        break;

                    case "display_name":
                        url.AppendFormat("&display_name={0}", value.Value);
                        break;

                    case "privacy":
                        if ((string.Compare(value.Value, "world", true) == 0 ||
                               string.Compare(value.Value, "only_friends", true) == 0 ||
                               string.Compare(value.Value, "only_me", true) == 0)) { return false; }
                        url.AppendFormat("&privacy={0}", value.Value);
                        break;

                    case "date_of_birth":
                        if (Regex.IsMatch(value.Value, @"\d{4}-\d{2}-\d{2}") == false) { return false; }
                        url.AppendFormat("&date_of_birth={0}", value.Value);
                        break;

                    default:
                        return false;
                }
            }

            string data;
            try { data = web.GetPage(url.ToString(), "post", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Update a user's profile picture. You can read more about how to render an avatar via user data. You should do a multipart/form-data POST request to /API/Users/updatePicture. The picture will be scaled down to 3 versions: big, medium and small. The optimal size of profile_image should be 195x195 pixels. 
        /// (NotImplementedException)
        /// </summary>
        /// <returns></returns>
        public bool UpdatePicture(string profile_image)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Profile

        /// <summary>
        /// Returns data that's private for the currently logged in user. This can be used to construct a profile and render a timeline of the latest plurks. 
        /// </summary>
        /// <returns></returns>
        public ownProfile getOwnProfile()
        {
            if (isLogged == false) { return null; }

            const string apiAction = "Profile/getOwnProfile";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            ownProfile obj;
            try { obj = new ownProfile(data); }
            catch (Exception) { return null; }

            return obj;
        }

        /// <summary>
        /// Fetches public information such as a user's public plurks and basic information. Fetches also if the current logged in user is following the user, are friends with or is a fan. 
        /// </summary>
        /// <param name="UseridOrNickname">The user_id of the public profile. Can be integer (like 34) or nick name (like amix).</param>
        /// <returns></returns>
        public publicProfile getPublicProfile(string UseridOrNickname)
        {
            const string apiAction = "Profile/getPublicProfile";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}", apiUrl, apiAction, apiKey, UseridOrNickname);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            publicProfile obj;
            try { obj = new publicProfile(data); }
            catch (Exception) { return null; }

            return obj;
        }

        #endregion

        #region Polling

        /// <summary>
        /// You should use this call to find out if there any new plurks posted to the user's timeline. It's much more efficient than doing it with /API/Timeline/getPlurks, so please use it :) 
        /// </summary>
        /// <param name="offset">Return plurks newer than offset, formatted as 2009-6-20T21:55:34. </param>
        /// <param name="limit"></param>
        /// <returns>The max number of plurks to be returned (default 50).</returns>
        public plurks getPlurks(DateTime offset, int limit)
        {
            var plurks = new plurks();
            if (isLogged == false) { return plurks; }

            const string apiAction = "Polling/getPlurks";
            var url = string.Format("{0}/{1}?api_key={2}&offset=[{3}]&limit={4}", apiUrl, apiAction, apiKey, offset.ToUniversalTime().ToString("s"), limit);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            var obj = JObject.Parse(data);
            if (obj["plurks"] == null) { return plurks; }

            var plurkObjs = JArray.Parse(obj["plurks"].ToString());
            plurks.AddRange(plurkObjs.Select(plurk => new plurk(plurk.ToString())));

            return plurks;
        }


        /// <summary>
        /// Use this call to find out if there are unread plurks on a user's timeline. 
        /// </summary>
        /// <returns></returns>
        public unreadCount getUnreadCount()
        {
            if (isLogged == false) { return null; }

            const string apiAction = "Polling/getUnreadCount";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            return new unreadCount(data);
        }

        #endregion

        #region Timeline

        /// <summary>
        /// Gets the plurk.
        /// </summary>
        /// <param name="plurk_id">The unique id of the plurk. Should be passed as a number, and not base 36 encoded. </param>
        /// <returns></returns>
        public plurk getPlurk(int plurk_id)
        {
            if (isLogged == false) { return null; }

            const string apiAction = "Timeline/getPlurk";
            var url = string.Format("{0}/{1}?api_key={2}&plurk_id={3}", apiUrl, apiAction, apiKey, plurk_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return null; }

            var obj = JObject.Parse(data);
            if (obj["plurk"] == null) { return null; }

            return new plurk(obj["plurk"].ToString());
        }

        /// <summary>
        /// Gets the plurks.
        /// </summary>
        /// <param name="offset">Return plurks older than offset, formatted as 2009-6-20T21:55:34.</param>
        /// <param name="limit">How many plurks should be returned? Default is 20.</param>
        /// <param name="filter">Can be only_user, only_responded or only_private.</param>
        /// <returns></returns>
        public plurks getPlurks(DateTime offset, int limit, string filter)
        {
            var plurks = new plurks();
            if (isLogged == false) { return plurks; }

            if ((string.Compare(filter, string.Empty) == 0 || string.Compare(filter, "only_user") == 0 ||
                 string.Compare(filter, "only_responded") == 0 || string.Compare(filter, "only_private") == 0) == false) { return plurks; }

            const string apiAction = "Timeline/getPlurks";
            var url = string.Format("{0}/{1}?api_key={2}&offset=[{3}]&limit={4}&filter={5}", apiUrl, apiAction, apiKey, offset.ToUniversalTime().ToString("s"), limit, filter);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            var obj = JObject.Parse(data);
            if (obj["plurks"] == null) { return plurks; }

            if (string.IsNullOrEmpty(data)) { return plurks; }

            var plurkObjs = JArray.Parse(obj["plurks"].ToString());
            plurks.AddRange(plurkObjs.Select(plurk => new plurk(plurk.ToString())));

            return plurks;
        }

        /// <summary>
        /// Gets the unread plurks.
        /// </summary>
        /// <param name="offset">Return plurks older than offset, formatted as 2009-6-20T21:55:34. </param>
        /// <param name="limit">Limit the number of plurks that is retunred. </param>
        /// <returns></returns>
        public plurks getUnreadPlurks(DateTime offset, int limit)
        {
            var plurks = new plurks();
            if (isLogged == false) { return plurks; }

            const string apiAction = "Timeline/getUnreadPlurks";
            var url = string.Format("{0}/{1}?api_key={2}&offset=[{3}]&limit={4}", apiUrl, apiAction, apiKey, offset.ToUniversalTime().ToString("s"), limit);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return plurks; }

            var obj = JObject.Parse(data);
            if (obj["plurks"] == null) { return plurks; }

            var plurkObjs = JArray.Parse(obj["plurks"].ToString());
            plurks.AddRange(plurkObjs.Select(plurk => new plurk(plurk.ToString())));

            return plurks;
        }

        /// <summary>
        /// Plurks the add.
        /// </summary>
        /// <param name="lang">The Plurk's text. </param>
        /// <param name="qualifier">The Plurk's qualifier</param>
        /// <param name="content">The content.</param>
        /// <param name="no_comments">
        ///  If set to 0, then everyone can respond to this plurk.
        ///  If set to 1, then responses are disabled for this plurk.
        ///  If set to 2, then only friends can respond to this plurk. 
        /// </param>
        /// <param name="limited_to">
        ///  Limit the plurk only to some users (also known as private plurking). limited_to should be a JSON list of friend ids, e.g. limited_to of [3,4,66,34] will only be plurked to these user ids.
        ///  1. string.empty => no limitation
        ///  2. only-friends => only friends can see
        ///  3. xxx|yyy|zzz => only xxx, yyy and zzz can see
        /// </param>
        /// <returns></returns>
        public int plurkAdd(lang lang, Qualifier qualifier, string content, string no_comments, string limited_to)
        {
            if (isLogged == false) { return -1; }

            const string apiAction = "Timeline/plurkAdd";
            var url = new StringBuilder(string.Format("{0}/{1}?api_key={2}&content={3}&qualifier={4}&no_comments={5}&lang={6}",
                                        apiUrl, apiAction, apiKey, HttpUtility.UrlEncode(content), GetVerb(qualifier), no_comments, lang));

            if (limited_to == "only-friends")
            {
                url.Append("&limited_to=[0]");
            }
            else if (limited_to != "")
            {
                url.AppendFormat("&limited_to={0}", GetLimitedTo(limited_to));
            }

            string data;
            try { data = web.GetPage(url.ToString(), "get", ref cookie, true); }
            catch { return -1; }

            if (string.IsNullOrEmpty(data)) { return -1; }

            plurk obj;
            try { obj = new plurk(data); }
            catch (Exception) { return -1; }

            return obj.plurk_id;
        }

        /// <summary>
        /// Plurks the delete.
        /// </summary>
        /// <param name="plurk_id">The id of the plurk. </param>
        /// <returns></returns>
        public bool plurkDelete(int plurk_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Timeline/plurkDelete";
            var url = string.Format("{0}/{1}?api_key={2}&plurk_id={3}", apiUrl, apiAction, apiKey, plurk_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Plurks the edit.
        /// </summary>
        /// <param name="plurk_id">The id of the plurk. </param>
        /// <param name="content">The content of plurk. </param>
        /// <returns></returns>
        public bool plurkEdit(int plurk_id, string content)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Timeline/plurkEdit";
            var url = string.Format("{0}/{1}?api_key={2}&content={3}&plurk_id={4}", 
                                     apiUrl, apiAction, apiKey, HttpUtility.UrlEncode(content), plurk_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Mutes the plurks.
        /// </summary>
        /// <param name="ids">The plurk ids, formated as JSON, e.g. [342,23242,2323]</param>
        /// <returns></returns>
        public bool mutePlurks(List<int> ids)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Timeline/mutePlurks";
            var url = string.Format("{0}/{1}?api_key={2}&ids={3}", apiUrl, apiAction, apiKey,
                                     string.Format("[{0}]",
                                                    string.Join(",", ids.Select(id => id.ToString()).ToArray())
                                                  )
                                   );

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Unmutes the plurks.
        /// </summary>
        /// <param name="ids">The plurk ids, formated as JSON, e.g. [342,23242,2323]</param>
        /// <returns></returns>
        public bool unmutePlurks(List<int> ids)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Timeline/unmutePlurks";
            var url = string.Format("{0}/{1}?api_key={2}&ids={3}", apiUrl, apiAction, apiKey,
                                     string.Format("[{0}]",
                                                    string.Join(",", ids.Select(id => id.ToString()).ToArray())
                                                  )
                                   );

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Marks as read.
        /// </summary>
        /// <param name="ids">The plurk ids, formated as JSON, e.g. [342,23242,2323]</param>
        /// <returns></returns>
        public bool markAsRead(List<int> ids)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Timeline/markAsRead";
            var url = string.Format("{0}/{1}?api_key={2}&ids={3}", apiUrl, apiAction, apiKey,
                                     string.Format("[{0}]",
                                                    string.Join(",", ids.Select(id => id.ToString()).ToArray())
                                                  )
                                   );

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        ///  To upload a picture to Plurk, you should do a multipart/form-data POST request to /API/Timeline/uploadPicture. This will add the picture to Plurk's CDN network and return a image link that you can add to /API/Timeline/plurkAdd Plurk will automatically scale down the image and create a thumbnail. 
        ///  (NotImplementedException)
        /// </summary>
        /// <returns></returns>
        public bool uploadPicture()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Responses

        /// <summary>
        /// Fetches responses for plurk with plurk_id and some basic info about the users. 
        /// </summary>
        /// <param name="plurk_id">The plurk that the responses should be added to. </param>
        /// <param name="from_response">Only fetch responses from an offset, should be 5, 10 or 15. </param>
        /// <returns></returns>
        public responses getResponses(int plurk_id, int from_response)
        {
            var responses = new responses();

            const string apiAction = "Responses/get";
            var url = string.Format("{0}/{1}?api_key={2}&plurk_id={3}&from_response={4}", apiUrl, apiAction, apiKey, plurk_id, from_response);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return responses; }

            if (string.IsNullOrEmpty(data)) { return responses; }

            var obj = JObject.Parse(data);
            if (obj["responses"] == null) { return responses; }

            var responseObjs = JArray.Parse(obj["responses"].ToString());
            responses.AddRange(responseObjs.Select(response => new response(response.ToString())));

            return responses;
        }

        /// <summary>
        /// Gets all responses.
        /// </summary>
        /// <param name="plurk_id">The plurk_id.</param>
        /// <returns></returns>
        public responses getAllResponses(int plurk_id)
        {
            var offset = 0;
            var responses = new responses();
            var tempResponses = getResponses(plurk_id, offset);
            while (tempResponses.Count != 0)
            {
                offset += 5;
                responses.AddRange(tempResponses);
                tempResponses = getResponses(plurk_id, offset);
            }
            return responses;
        }

        /// <summary>
        /// Adds a responses to plurk_id. Language is inherited from the plurk. 
        /// </summary>
        /// <param name="qualifier">The Plurk's qualifier,</param>
        /// <param name="content">The response's text. </param>
        /// <param name="plurk_id">The plurk_id.</param>
        /// <returns></returns>
        public bool responseAdd(Qualifier qualifier, string content, int plurk_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Responses/responseAdd";
            var url = string.Format("{0}/{1}?api_key={2}&qualifier={3}&content={4}&plurk_id={5}",
                                     apiUrl, apiAction, apiKey, GetVerb(qualifier), HttpUtility.UrlEncode(content), plurk_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            var obj = new response(data);
            return (obj.content_raw == content) ? true : false;
        }

        /// <summary>
        /// Deletes a response. A user can delete own responses or responses that are posted to own plurks. 
        /// </summary>
        /// <param name="response_id">The plurk that the responses should be added to. </param>
        /// <param name="plurk_id">The plurk that the response belongs to. </param>
        /// <returns></returns>
        public bool responseDelete(int response_id, int plurk_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Responses/responseDelete";
            var url = string.Format("{0}/{1}?api_key={2}&response_id={3}&plurk_id={4}", apiUrl, apiAction, apiKey, response_id, plurk_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_tex")) ? true : false;
        }

        #endregion

        #region Friends and fans

        /// <summary>
        /// Returns user_id's friend list in chucks of 10 friends at a time. 
        /// </summary>
        /// <param name="user_id">The user_id.</param>
        /// <param name="offset">The offset, can be 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos getFriends(int user_id, int offset)
        {
            var user_infos = new user_infos();

            const string apiAction = "FriendsFans/getFriendsByOffset";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}&offset={4}", apiUrl, apiAction, apiKey, user_id, offset);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return new user_infos(); }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var userObjs = JArray.Parse(data);
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Returns user_id's friend list in chucks of 10 friends at a time. (requires login)
        /// </summary>
        /// <param name="offset">The offset, can be 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos getFriends(int offset)
        {
            var user_infos = new user_infos();
            return isLogged == false ? user_infos : getFriends(uid, offset);
        }

        /// <summary>
        /// Gets all friends.
        /// </summary>
        /// <param name="user_id">The user_id.</param>
        /// <returns></returns>
        public user_infos getAllFriends(int user_id)
        {
            var offset = 0;
            var userInfos = new user_infos();
            var tempUserInfos = getFriends(user_id, offset);
            while (tempUserInfos.Count != 0)
            {
                offset += 10;
                userInfos.AddRange(tempUserInfos);
                tempUserInfos = getFriends(user_id, offset);
            }
            return userInfos;
        }

        /// <summary>
        /// Gets all friends. (requires login)
        /// </summary>
        /// <returns></returns>
        public user_infos getAllFriends()
        {
            var user_infos = new user_infos();
            return isLogged == false ? user_infos : getAllFriends(uid);
        }

        /// <summary>
        /// Returns user_id's fans list in chucks of 10 fans at a time. 
        /// </summary>
        /// <param name="user_id">The user_id.</param>
        /// <param name="offset">The offset, can be 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos getFans(int user_id, int offset)
        {
            var user_infos = new user_infos();

            const string apiAction = "FriendsFans/getFansByOffset";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}&offset={4}", apiUrl, apiAction, apiKey, user_id, offset);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return new user_infos(); }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var userObjs = JArray.Parse(data);
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Gets the fans.  (requires login)
        /// </summary>
        /// <param name="offset">The offset, can be 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos getFans(int offset)
        {
            var user_infos = new user_infos();
            return isLogged == false ? user_infos : getFans(uid, offset);
        }

        /// <summary>
        /// Gets all fans.
        /// </summary>
        /// <param name="user_id">The user_id.</param>
        /// <returns></returns>
        public user_infos getAllFans(int user_id)
        {
            var offset = 0;
            var userInfos = new user_infos();
            var tempUserInfos = getFans(user_id, offset);
            while (tempUserInfos.Count != 0)
            {
                offset += 10;
                userInfos.AddRange(tempUserInfos);
                tempUserInfos = getFans(user_id, offset);
            }
            return userInfos;
        }

        /// <summary>
        /// Gets all fans.  (requires login)
        /// </summary>
        /// <returns></returns>
        public user_infos getAllFans()
        {
            var user_infos = new user_infos();
            return isLogged == false ? user_infos : getAllFans(uid);
        }

        /// <summary>
        /// Returns users that the current logged in user follows as fan - in chucks of 10 fans at a time. 
        /// </summary>
        /// <param name="offset">The offset, can be 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos getFollowings(int offset)
        {
            var user_infos = new user_infos();
            if (isLogged == false) { return user_infos; }

            const string apiAction = "FriendsFans/getFollowingByOffset";
            var url = string.Format("{0}/{1}?api_key={2}&offset={3}", apiUrl, apiAction, apiKey, offset);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return new user_infos(); }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var userObjs = JArray.Parse(data);
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Gets all followings.
        /// </summary>
        /// <returns></returns>
        public user_infos getAllFollowings()
        {
            var user_infos = new user_infos();
            if (isLogged == false) { return user_infos; }

            var offset = 0;
            var tempUserInfos = getFollowings(offset);
            while (tempUserInfos.Count != 0)
            {
                offset += 10;
                user_infos.AddRange(tempUserInfos);
                tempUserInfos = getFollowings(offset);
            }
            return user_infos;
        }

        /// <summary>
        /// Create a friend request to friend_id. User with friend_id has to accept a friendship. 
        /// </summary>
        /// <param name="friend_id">The ID of the user you want to befriend. </param>
        /// <returns></returns>
        public bool becomeFriend(int friend_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "FriendsFans/becomeFriend";
            var url = string.Format("{0}/{1}?api_key={2}&friend_id={3}", apiUrl, apiAction, apiKey, friend_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Remove friend with ID friend_id. friend_id won't be notified. 
        /// </summary>
        /// <param name="friend_id">The ID of the user you want to remove </param>
        /// <returns></returns>
        public bool removeFriend(int friend_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "FriendsFans/removeAsFriend";
            var url = string.Format("{0}/{1}?api_key={2}&friend_id={3}", apiUrl, apiAction, apiKey, friend_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Become fan of fan_id. To stop being a fan of someone, user /API/FriendsFans/setFollowing?fan_id=FAN_ID&follow=false. 
        /// </summary>
        /// <param name="fan_id">The ID of the user you want to become fan of </param>
        /// <returns></returns>
        public bool becomeFan(int fan_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "FriendsFans/becomeFan";
            var url = string.Format("{0}/{1}?api_key={2}&fan_id={3}", apiUrl, apiAction, apiKey, fan_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Update following of user_id. A user can befriend someone, but can unfollow them. This request is also used to stop following someone as a fan. 
        /// </summary>
        /// <param name="user_id">The ID of the user you want to follow/unfollow </param>
        /// <param name="follow">
        ///  true if the user should be followed, 
        ///  false if the user should be unfollowed. 
        /// </param>
        /// <returns></returns>
        public bool setFollowing(int user_id, bool follow)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "FriendsFans/setFollowing";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}&follow={4}", apiUrl, apiAction, apiKey, user_id, follow.ToString().ToLower());

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("error_text")) ? false : true;
        }

        /// <summary>
        /// Returns a JSON object of the logged in users friends (nick name and full name). This information can be used to construct auto-completion for private plurking. Notice that a friend list can be big, depending on how many friends a user has, so this list should be lazy-loaded in your application.
        /// </summary>
        /// <returns></returns>
        public List<int> getCompletion()
        {
            var friendsAndFansUserIDList = new List<int>();
            if (isLogged == false) { return friendsAndFansUserIDList; }

            const string apiAction = "FriendsFans/getCompletion";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return friendsAndFansUserIDList; }

            if (string.IsNullOrEmpty(data)) { return friendsAndFansUserIDList; }

            var matches = Regex.Matches(data, @"""id"": (\d+),");
            friendsAndFansUserIDList.AddRange(from Match match in matches
                                              where match.Success
                                              select int.Parse(match.Groups[1].Value));

            return friendsAndFansUserIDList;
        }

        #endregion

        #region Alerts

        /// <summary>
        /// Return a JSON list of current active alerts.    
        /// </summary>
        /// <returns></returns>
        public List<alert> getActive()
        {
            var alerts = new List<alert>();
            if (isLogged == false) { return alerts; }

            const string apiAction = "Alerts/getActive";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return alerts; }

            if (string.IsNullOrEmpty(data)) { return alerts; }

            var obj = JArray.Parse(data);
            alerts.AddRange(obj.Select(alert => new alert(alert.ToString())));

            return alerts;
        }

        /// <summary>
        /// Gets the Friendship request.
        /// </summary>
        /// <returns></returns>
        public List<alert> getFriendRequests()
        {
            var alerts = new List<alert>();
            if (isLogged == false) { return alerts; }

            const string apiAction = "Alerts/getActive";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return alerts; }

            if (string.IsNullOrEmpty(data)) { return alerts; }

            var obj = JArray.Parse(data);
            alerts.AddRange(obj.Where(alert => alert["from_user"] != null).Select(alert => new alert(alert.ToString())));

            return alerts;
        }

        /// <summary>
        /// Return a JSON list of past 30 alerts. 
        /// </summary>
        /// <returns></returns>
        public List<alert> getHistory()
        {
            var alerts = new List<alert>();
            if (isLogged == false) { return alerts; }

            const string apiAction = "Alerts/getHistory";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return new List<alert>(); }

            if (string.IsNullOrEmpty(data)) { return alerts; }

            var obj = JArray.Parse(data);
            alerts.AddRange(obj.Where(alert => alert["from_user"] != null).Select(alert => new alert(alert.ToString())));

            return alerts;
        }

        /// <summary>
        /// Accept user_id as fan. 
        /// </summary>
        /// <param name="user_id">The user_id that has asked for friendship. </param>
        /// <returns></returns>
        public bool addAsFan(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/addAsFan";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Accept all friendship requests as fans. 
        /// </summary>
        /// <returns></returns>
        public bool addAllAsFan()
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/addAllAsFan";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Accept all friendship requests as friends. 
        /// </summary>
        /// <returns></returns>
        public bool addAllAsFriends()
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/addAllAsFriends";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Accept user_id as friend. 
        /// </summary>
        /// <param name="user_id">The user_id that has asked for friendship. </param>
        /// <returns></returns>
        public bool addAsFriend(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/addAsFriend";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Deny friendship to user_id. 
        /// </summary>
        /// <param name="user_id">The user_id that has asked for friendship. </param>
        /// <returns></returns>
        public bool denyFriendship(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/denyFriendship";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Remove notification to user with id user_id. 
        /// </summary>
        /// <param name="user_id">The user_id that the current user has requested friendship for. </param>
        /// <returns></returns>
        public bool removeNotification(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Alerts/removeNotification";
            var url = string.Format("{0}/{1}?api_key={2}&user_id={3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        #endregion

        #region Search

        /// <summary>
        /// Returns the latest 20 plurks on a search term. 
        /// </summary>
        /// <param name="query">The query after Plurks. </param>
        /// <returns></returns>
        public plurks plurkSearch(string query)
        {
            return plurkSearch(query, -1);
        }

        /// <summary>
        /// Returns the latest 20 plurks on a search term. 
        /// </summary>
        /// <param name="query">The query after Plurks. </param>
        /// <param name="plurk_id">
        ///  A plurk_id of the oldest Plurk in the last search result.
        ///  -1 => no limitation
        /// </param>
        /// <returns></returns>
        public plurks plurkSearch(string query, int plurk_id)
        {
            var plurks = new plurks();

            const string apiAction = "PlurkSearch/search";
            var url = string.Format("{0}/{1}?api_key={2}&query={3}", apiUrl, apiAction, apiKey, query);

            if (plurk_id != -1) { url += string.Format("&offset={0}", plurk_id); }

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            var obj = JObject.Parse(data);
            if (obj["plurks"] == null) { return plurks; }

            if (string.IsNullOrEmpty(data)) { return plurks; }

            var plurkObjs = JArray.Parse(obj["plurks"].ToString());
            plurks.AddRange(plurkObjs.Select(plurk => new plurk(plurk.ToString())));

            return plurks;
        }

        /// <summary>
        /// Returns 10 users that match query, users are sorted by karma. 
        /// </summary>
        /// <param name="query">The query after users. </param>
        /// <param name="offset">Page offset, like 10, 20, 30 etc. </param>
        /// <returns></returns>
        public user_infos userSearch(string query, int offset)
        {
            var user_infos = new user_infos();

            const string apiAction = "UserSearch/search";
            var url = string.Format("{0}/{1}?api_key={2}&query={3}&offset={4}", apiUrl, apiAction, apiKey, query, offset);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return null; }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var obj = JObject.Parse(data);
            if (obj["users"] == null) { return user_infos; }

            var userObjs = JArray.Parse(obj["users"].ToString());
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Alls the user search. (not recommended to use)
        /// </summary>
        /// <param name="query">The query after users. </param>
        /// <returns></returns>
        public user_infos allUserSearch(string query)
        {
            var offset = 0;
            var userInfos = new user_infos();
            var tempUserInfos = userSearch(query, offset);
            while (tempUserInfos.Count != 0)
            {
                offset += 10;
                userInfos.AddRange(tempUserInfos);
                tempUserInfos = userSearch(query, offset);
            }
            return userInfos;
        }

        #endregion

        #region Emoticons

        /// <summary>
        /// Emoticons are a big part of Plurk since they make it easy to express feelings (NotImplementedException)
        /// </summary>
        /// <returns></returns>
        public string getEmotions()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region Blocks

        /// <summary>
        /// Gets the blocks.
        /// </summary>
        /// <param name="offset">What page should be shown, e.g. 0, 10, 20. </param>
        /// <returns></returns>
        public user_infos getBlocks(int offset)
        {
            var user_infos = new user_infos();
            if (isLogged == false) { return user_infos; }

            const string apiAction = "Blocks/get";
            var url = string.Format("{0}/{1}?api_key={2}&offset={3}", apiUrl, apiAction, apiKey, offset);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return user_infos; }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var obj = JObject.Parse(data);
            if (obj["users"] == null) { return user_infos; }

            var userObjs = JArray.Parse(obj["users"].ToString());
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Gets all blocks.
        /// </summary>
        /// <returns></returns>
        public user_infos getAllBlocks()
        {
            var offset = 0;
            var userInfos = new user_infos();
            var tempUserInfos = getBlocks(offset);
            while (tempUserInfos.Count != 0)
            {
                offset += 5;
                userInfos.AddRange(tempUserInfos);
                tempUserInfos = getBlocks(offset);
            }
            return userInfos;
        }

        /// <summary>
        /// Blocks the specified user_id.
        /// </summary>
        /// <param name="user_id">The id of the user that should be blocked. </param>
        /// <returns></returns>
        public bool block(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Blocks/block";
            var url = string.Format("{0}/{1}?api_key={2}&user_id{3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Unblocks the specified user_id.
        /// </summary>
        /// <param name="user_id">The id of the user that should be unblocked. </param>
        /// <returns></returns>
        public bool unblock(int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Blocks/unblock";
            var url = string.Format("{0}/{1}?api_key={2}&user_id{3}", apiUrl, apiAction, apiKey, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        #endregion

        #region Cliques

        /// <summary>
        /// Gets the cliques.
        /// </summary>
        /// <returns></returns>
        public List<string> getCliques()
        {
            var cliques = new List<string>();
            if (isLogged == false) { return cliques; }

            const string apiAction = "Cliques/getCliques";
            var url = string.Format("{0}/{1}?api_key={2}", apiUrl, apiAction, apiKey);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return cliques; }

            if (string.IsNullOrEmpty(data)) { return cliques; }

            var obj = JArray.Parse(data);
            cliques.AddRange(obj.Select(cliqueName => cliqueName.ToString()));

            return cliques;
        }

        /// <summary>
        /// Gets the clique.
        /// </summary>
        /// <param name="clique_name">The name of the new clique </param>
        /// <returns></returns>
        public user_infos getClique(string clique_name)
        {
            var user_infos = new user_infos();
            if (isLogged == false) { return user_infos; }

            const string apiAction = "Cliques/getClique";
            var url = string.Format("{0}/{1}?api_key={2}&clique_name={3}", apiUrl, apiAction, apiKey, clique_name);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return new user_infos(); }

            if (string.IsNullOrEmpty(data)) { return user_infos; }

            var userObjs = JArray.Parse(data);
            user_infos.AddRange(userObjs.Select(userinfo => new user_info(userinfo.ToString())));

            return user_infos;
        }

        /// <summary>
        /// Creates the clique.
        /// </summary>
        /// <param name="clique_name">The name of the new clique </param>
        /// <returns></returns>
        public bool createClique(string clique_name)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Cliques/createClique";
            var url = string.Format("{0}/{1}?api_key={2}&clique_name={3}", apiUrl, apiAction, apiKey, clique_name);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Renames the clique.
        /// </summary>
        /// <param name="clique_name">The name of the clique to rename </param>
        /// <param name="new_name">The name of the new clique </param>
        /// <returns></returns>
        public bool renameClique(string clique_name, string new_name)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Cliques/renameClique";
            var url = string.Format("{0}/{1}?api_key={2}&clique_name={3}&new_name={4}", apiUrl, apiAction, apiKey, clique_name, new_name);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Adds the clique.
        /// </summary>
        /// <param name="clique_name">The name of the clique </param>
        /// <param name="user_id">The user to add to the clique </param>
        /// <returns></returns>
        public bool addClique(string clique_name, int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Cliques/add";
            var url = string.Format("{0}/{1}?api_key={2}&clique_name={3}&user_id={4}", apiUrl, apiAction, apiKey, clique_name, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        /// <summary>
        /// Removes the clique.
        /// </summary>
        /// <param name="clique_name">The name of the clique </param>
        /// <param name="user_id">The user to remove from the clique </param>
        /// <returns></returns>
        public bool removeClique(string clique_name, int user_id)
        {
            if (isLogged == false) { return false; }

            const string apiAction = "Cliques/remove";
            var url = string.Format("{0}/{1}?api_key={2}&clique_name={3}&user_id={4}", apiUrl, apiAction, apiKey, clique_name, user_id);

            string data;
            try { data = web.GetPage(url, "get", ref cookie, true); }
            catch { return false; }

            if (string.IsNullOrEmpty(data)) { return false; }

            return (data.Contains("success_text")) ? true : false;
        }

        #endregion

        /// <summary>
        /// get the user id by nickname
        /// </summary>
        /// <param name="UseridOrNickname">The userid or nickname.</param>
        /// <returns></returns>        
        public int GetUserID(string UseridOrNickname)
        {
            var profile = getPublicProfile(UseridOrNickname);
            if (profile == null) { return -1; }

            return profile.user_info.uid;
        }

        /// <summary>
        /// Gets the limited to.
        /// </summary>
        /// <param name="userIDs">The user I ds.</param>
        /// <returns></returns>
        private string GetLimitedTo(string userIDs)
        {
            if (string.IsNullOrEmpty(userIDs)) { return string.Empty; }

            var ids = userIDs.Split("|".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

            var length = ids.Length;
            if (ids.Length < 1) { return string.Empty; }

            for (var i = 0; i < length; i++)
            {
                ids[i] = GetUserID(ids[i]).ToString();
            }

            // [3,4,66,34] 
            return string.Format("[{0}]", string.Join(",", ids));
        }

        /// <summary>
        /// Gets the pages.
        /// </summary>
        /// <param name="url">The URL.</param>
        /// <returns></returns>
        public string GetPages(string url)
        {
            string data;
            try
            {
                data = web.GetPage(url, "get", ref cookie, true);
            }
            catch (Exception)
            {

                data = string.Empty;
            }

            return data;
        }

        /// <summary>
        /// get the verb of plurk qualifier
        /// </summary>
        /// <param name="qualifier"></param>
        /// <returns></returns>
        private static string GetVerb(Qualifier qualifier)
        {
            return (qualifier == 0) ? ":" : qualifier.ToString();
        }
    }
}